#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
语音助手Demo
功能：
1. 等待唤醒词检测
2. 检测到唤醒词后播放系统音效
3. 开始基于VAD的录音（3秒间隔）
4. 语音转文字
5. 循环进行VAD录音和识别
"""
import time
import threading
from packages.voice_module.voice_module.voice_interface import VoiceInterface
from packages.voice_module.voice_module.core.audio_queue_manager import AudioPriority
import requests

DIFY_IP_ADDR = "http://192.168.12.57"
DIFY_API_KEY = "app-eLGYgnsVzu1eClNm0PPYtA7o"
class VoiceAssistantDemo:
    def __init__(self):
        # 指定配置文件路径以确保语音识别模块正确初始化
        config_path = "packages/voice_module/voice_module/config/voice_config.yaml"
        self.voice = VoiceInterface(config_path=config_path)
        self.is_running = False
        self.wake_detected = False
        self.wake_thread = None
        # 后端处理状态与中断控制
        self.backend_processing = False
        self.interrupt_event = threading.Event()
        self.llm_thread = None
        # 可播报字段候选列表（按优先级）
        self.SPEAKABLE_KEYS = [
            "tts_text", "speech", "assistant_reply", "reply",
            "final_answer", "text", "answer", "say"
        ]

    # ---------------------- 内部工具方法（减少重复逻辑） ----------------------
    def _is_audio_playing(self) -> bool:
        """检查当前是否有音频正在播放（系统音效或TTS）。"""
        try:
            aqm = getattr(self.voice, 'audio_queue_manager', None)
            player = getattr(self.voice, 'audio_player', None)
            if aqm and getattr(aqm, 'is_playing', False):
                return True
            if player and getattr(player, 'is_playing', False):
                return True
        except Exception:
            pass
        return False

    def _safe_stop_recording(self):
        """安全停止录音，忽略异常。"""
        try:
            self.voice.stop_recording()
        except:
            pass

    def _post_workflow(self, text: str, timeout=(5, 180)):
        """向后端工作流发送请求，返回响应字典或None。"""
        try:
            url = f"{DIFY_IP_ADDR}/v1/workflows/run"
            headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {DIFY_API_KEY}"
            }
            data = {
                "inputs": {"input_text": text},
                "response_mode": "blocking",
                "user": "abc-123"
            }
            response = requests.post(url, headers=headers, json=data, timeout=timeout) if timeout else requests.post(url, headers=headers, json=data)
            response_dict = response.json()
            print(f"🔍 接收到响应: {response_dict}")
            return response_dict
        except Exception as e:
            print(f"❌ 请求时发生错误: {e}")
            return None

    def _extract_speakable_text(self, response_dict) -> str:
        """从响应中提取可播报文本，不存在则返回空字符串。"""
        try:
            data_obj = response_dict.get("data", {}) if isinstance(response_dict, dict) else {}
            outputs = data_obj.get("outputs", {}) if isinstance(data_obj, dict) else {}
            if isinstance(outputs, dict):
                for k in self.SPEAKABLE_KEYS:
                    val = outputs.get(k)
                    if isinstance(val, str) and val.strip():
                        return val.strip()
        except Exception as parse_err:
            print(f"[DEBUG] 解析响应可播报内容失败: {parse_err}")
        return ""
        
    def initialize(self):
        """初始化所有语音模块"""
        print("正在初始化语音助手...")
        
        # 添加配置调试信息
        print(f"🔧 配置文件路径: {getattr(self.voice, 'config_path', 'Unknown')}")
        
        # 检查STT配置
        stt_config = self.voice.config.get('speech_to_text', {})
        print(f"🔧 STT配置存在: {bool(stt_config)}")
        if stt_config:
            print(f"🔧 STT app_id: {stt_config.get('app_id', 'Missing')[:8]}...")
            print(f"🔧 STT api_key: {stt_config.get('api_key', 'Missing')[:8]}...")
            print(f"🔧 STT api_secret: {stt_config.get('api_secret', 'Missing')[:8]}...")
        
        # 初始化所有模块
        success, message, _ = self.voice.initialize_all()
        if not success:
            print(f"初始化失败: {message}")
            return False
        
        # 检查模块状态
        print("🔍 检查各模块状态:")
        status_success, status_msg, module_status = self.voice.get_module_status()
        if status_success:
            for module, is_ready in module_status.items():
                status_icon = "✅" if is_ready else "❌"
                print(f"  {status_icon} {module}: {'已就绪' if is_ready else '未就绪'}")
        
        # 特别检查STT客户端
        print(f"🔍 STT客户端对象: {type(getattr(self.voice, 'stt_client', None))}")
        print(f"🔍 STT客户端存在: {hasattr(self.voice, 'stt_client') and self.voice.stt_client is not None}")
        
        if hasattr(self.voice, 'stt_client') and self.voice.stt_client:
            stt_client = self.voice.stt_client
            print(f"🔍 STT客户端配置: app_id={getattr(stt_client, 'app_id', 'Missing')[:8]}...")
            
        print("语音助手初始化成功！")
        return True
    
    def single_voice_interaction(self):
        """单次语音交互（唤醒后的一次问答）"""
        try:
            # 播放录音开始音效（阻塞播放，确保完全播放完毕）
            print("\n🔴 开始录音...")
            self.voice.play_system_sound("alert_start_records", blocking=True)
            time.sleep(0.1)  # 进一步减少延迟，快速进入录音状态
            
            # 启动VAD单段录制（录制完一段后自动停止）
            success, message, _ = self.voice.start_single_segment_recording(
                base_filename=f"demo_segment_{int(time.time())}"
            )
            
            if not success:
                print(f"❌ 启动录音失败: {message}")
                self.voice.play_system_sound("error")
                return
            
            print("🎤 正在录音中，检测到静音后自动停止...")
            
            # 等待录音完成
            while True:
                success_status, _, is_recording = self.voice.is_recording()
                if not success_status or not is_recording:
                    break
                time.sleep(0.1)
            
            # 获取录音结果
            success_stop, message_stop, result_file = self.voice.stop_recording()
            
            if success_stop and result_file:
                # 播放录音结束音效
                self.voice.play_system_sound("alert_stop_record")
                print(f"📹 录制完成: {result_file}")
                
                # 语音识别
                if hasattr(self.voice, 'stt_client') and self.voice.stt_client:
                    print("🔄 正在进行语音识别...")
                    success_rec, message_rec, text = self.voice.recognize_audio_file(result_file)
                    
                    if success_rec and text and text.strip():
                        print(f"✅ 识别结果: {text}")
                        self.voice.play_system_sound("success")
                        # 启动后端处理（异步）
                        self.start_backend_processing(text)
                        # 继续等待下一次唤醒
                        return True
                    else:
                        # 语音识别失败或识别内容为空
                        if success_rec and (not text or not text.strip()):
                            print("❌ 语音识别内容为空")
                        else:
                            print(f"❌ 语音识别失败: {message_rec}")
                        # 播放"请再说一遍"提示音
                        self.voice.play_system_sound("sentence_pardon")
                        return True  # 识别失败也继续循环
                else:
                    print("⚠️ 语音识别模块未初始化")
                    return True  # 模块未初始化也继续循环
            else:
                print(f"❌ 录音失败: {message_stop}")
                self.voice.play_system_sound("error")
                return True  # 录音失败也继续循环
                
        except Exception as e:
            print(f"语音交互过程中发生错误: {e}")
            self.voice.play_system_sound("error")
            try:
                self.voice.stop_recording()
            except:
                pass
            return True  # 发生异常也继续循环
    
    def wake_detection_loop(self):
        """唤醒词检测循环"""
        print("[DEBUG] wake_detection_loop started")
        while self.is_running:
            try:
                # 检查是否检测到唤醒
                success, message, detected = self.voice.is_wake_detected()
                if not success:
                    print(f"[DEBUG] is_wake_detected failed: {message}")
                elif detected:
                    print("\n🎯 检测到唤醒词！")
                    self.wake_detected = True
                    
                    # 如果后台还在处理中或当前正在播放音频，触发中断并重置状态
                    if self.backend_processing or self._is_audio_playing():
                        self.handle_interrupt()
                    # 播放唤醒音效（阻塞播放，确保完全播放完毕）
                    print("播放唤醒音效...")
                    self.voice.play_system_sound("sentence_i_am_here", blocking=True)
                    # 进行单次语音交互（录音期间会自动暂停唤醒检测）
                    should_continue = self.single_voice_interaction()
                    
                    # 检查是否需要退出
                    if should_continue == False:
                        print("[DEBUG] User requested exit, stopping wake detection loop")
                        break
                    
                    # 继续等待下一次唤醒词（唤醒检测线程保持运行）
                    print("\n🎤 等待下一次唤醒词...")
                    self.wake_detected = False
                else:
                    # 每10秒输出一次状态
                    if hasattr(self, '_debug_counter'):
                        self._debug_counter += 1
                    else:
                        self._debug_counter = 1
                    
                    if self._debug_counter % 100 == 0:  # 每10秒(100*0.1s)输出一次
                        print(f"[DEBUG] Wake detection loop running, no wake detected (count: {self._debug_counter})")
                    
                time.sleep(0.1)  # 避免过度占用CPU
                
            except Exception as e:
                print(f"[DEBUG] Exception in wake_detection_loop: {e}")
                import traceback
                traceback.print_exc()
                time.sleep(1)

    def start_backend_processing(self, text: str):
        """启动与大模型后端的交互处理（异步）"""
        try:
            self.backend_processing = True
            self.interrupt_event.clear()
            self.llm_thread = threading.Thread(target=self._backend_worker, args=(text,), daemon=True)
            self.llm_thread.start()
            print("🔧 已启动后端处理线程")
        except Exception as e:
            print(f"❌ 启动后端处理线程失败: {e}")
            self.backend_processing = False

    def _backend_worker(self, text: str):
        """后端处理线程：调用LLM服务并播放回复，支持被中断"""
        response_text = ""
        try:
            if self.interrupt_event.is_set():
                print("⏹️ 后端处理已被中断（请求未发出）")
                return
            # 设置超时以避免长时间阻塞（连接5秒，读取180秒）
            response_dict = self._post_workflow(text, timeout=(5, 180))
            if response_dict:
                response_text = self._extract_speakable_text(response_dict)
                if not response_text:
                    print("ℹ️ 工作流无可播报文本（outputs为空或未包含文本字段），跳过语音播放")
        finally:
            # 如果在网络期间被标记为中断，则不进行后续播放
            if self.interrupt_event.is_set():
                print("⏹️ 后端处理已被中断（不播放回复）")
                self.backend_processing = False
                return
            # 播放回复（使用队列以便后续可被打断）
            if response_text:
                self.speak_response(response_text)
            self.backend_processing = False

    def handle_interrupt(self):
        """执行硬中断：停止当前所有动作、清空队列并重置状态"""
        try:
            print("⚠️ 检测到再次唤醒，正在打断当前流程并重置状态...")
            # 标记中断（后台线程看到后会停止后续动作）
            self.interrupt_event.set()
            # 通知ROS2控制器触发全局取消（如果HTTP服务已启动）
            try:
                requests.post("http://10.241.251.165:8080/controller/cancel", timeout=2)
            except Exception as e:
                print(f"[DEBUG] 通知控制器取消失败（可能服务未启动或不可达）: {e}")
            # 立即停止当前播放（包括系统音效/TTS），并清空队列
            self.voice.stop_audio(force=True)
            try:
                self.voice.interrupt_audio_playback()
            except Exception as e:
                print(f"[DEBUG] interrupt_audio_playback error: {e}")
            # 停止录音（如果正在录音）
            self._safe_stop_recording()
            # 重置后端处理标记（线程可能仍在网络等待，但不再触发播放）
            self.backend_processing = False
            print("✅ 已完成中断与状态重置")
        except Exception as e:
            print(f"❌ 处理中断时发生错误: {e}")
    
    def start_recording_cycle(self):
        """开始录音识别循环（用于Windows测试环境）"""
        print("\n📝 开始语音录音识别循环...")
        
        while True:
            try:
                # 播放录音开始音效
                print("\n🔴 开始录音...")
                self.voice.play_system_sound("start_record")
                time.sleep(0.5)  # 等待音效播放完成
                
                # 启动VAD单段录制（录制完一段后自动停止）
                success, message, _ = self.voice.start_single_segment_recording(
                    base_filename=f"demo_segment_{int(time.time())}"
                )
                
                if not success:
                    print(f"❌ 启动录音失败: {message}")
                    self.voice.play_system_sound("error")
                    break
                
                print("🎤 正在录音中，检测到静音后自动停止...")
                print("💡 按Ctrl+C可手动停止录音")
                
                # 等待录音完成或用户中断
                try:
                    # 监听录音状态
                    while True:
                        success_status, _, is_recording = self.voice.is_recording()
                        if not success_status or not is_recording:
                            break
                        time.sleep(0.1)  # 减少检查间隔，提高响应速度
                        
                except KeyboardInterrupt:
                    print("\n⏹️ 用户手动停止录音")
                    self.voice.stop_recording()  # 确保停止录音
                    
                # 获取录音结果（单段录音自动停止后获取结果）
                success_stop, message_stop, result_file = self.voice.stop_recording()
                
                if success_stop and result_file:
                    # 播放录音结束音效
                    self.voice.play_system_sound("stop_record")
                    
                    print(f"📹 录制完成: {result_file}")
                    
                    # 语音转文字 - 添加详细调试信息
                    print(f"🔍 检查STT模块状态:")
                    print(f"  - hasattr(self.voice, 'stt_client'): {hasattr(self.voice, 'stt_client')}")
                    print(f"  - self.voice.stt_client: {getattr(self.voice, 'stt_client', None)}")
                    print(f"  - stt_client type: {type(getattr(self.voice, 'stt_client', None))}")
                    
                    if hasattr(self.voice, 'stt_client'):
                        stt_client = self.voice.stt_client
                        print(f"  - stt_client is not None: {stt_client is not None}")
                        if stt_client:
                            print(f"  - stt_client.app_id: {getattr(stt_client, 'app_id', 'Missing')[:8]}...")
                    
                    if hasattr(self.voice, 'stt_client') and self.voice.stt_client:
                        print("🔄 正在进行语音识别...")
                        success_rec, message_rec, text = self.voice.recognize_audio_file(result_file)
                        
                        if success_rec and text:
                            print(f"✅ 识别结果: {text}")
                            
                            # 这里可以添加对识别结果的处理逻辑
                            should_continue = self.process_voice_command(text)
                            if not should_continue:
                                # 用户要求退出，停止录音循环
                                break
                            
                        else:
                            print(f"❌ 语音识别失败: {message_rec}")
                            self.voice.play_system_sound("error")
                    else:
                        print("⚠️ 语音识别模块未初始化")
                        print(f"   原因分析: hasattr={hasattr(self.voice, 'stt_client')}, stt_client={getattr(self.voice, 'stt_client', None)}")
                    
                    # 询问是否继续
                    print("\n💬 请继续说话，或按Ctrl+C退出录音模式")
                    
                elif success_stop and not result_file:
                    print("⏰ 录音超时，未检测到语音")
                    break
                else:
                    print(f"❌ 停止录音失败: {message_stop}")
                    self.voice.play_system_sound("error")
                    break
                    
            except KeyboardInterrupt:
                print("\n👋 用户中断录音模式")
                # 确保停止录音
                self._safe_stop_recording()
                break
            except Exception as e:
                print(f"录音识别过程中发生错误: {e}")
                self.voice.play_system_sound("error")
                # 确保停止录音
                self._safe_stop_recording()
                break
    
    def process_voice_command(self, text):
        """处理语音命令（可扩展）
        
        Returns:
            bool: True表示继续循环等待下一次唤醒，False表示退出程序
        """
        print(f"🤖 处理语音命令: {text}")
        
        # 简化的HTTP调用（保持原有行为：仅打印响应，不做播报）
        _ = self._post_workflow(text, timeout=None)
        
        # 默认继续循环等待下一次唤醒
        return True

        
    
    def speak_response(self, text):
        """将文本转换为语音并播放（通过队列，便于打断）"""
        try:
            print(f"🔊 正在播放语音回复: {text}")
            # 使用队列播放TTS，以支持后续硬中断
            success, message, _ = self.voice.play_synthesized_speech(
                text,
                volume=None,
                priority=AudioPriority.NORMAL,
                use_queue=True
            )
            if success:
                print("🎵 语音播放任务已提交到队列")
            else:
                print(f"❌ 语音播放任务提交失败: {message}")
        except Exception as e:
            print(f"❌ 语音回复过程中发生错误: {e}")
    
    def start(self):
        """启动语音助手"""
        if not self.initialize():
            return
        
        self.is_running = True
        
        try:
            # 尝试启动唤醒检测
            success, message, _ = self.voice.start_wake_detection()
            if not success:
                print(f"⚠️ 启动唤醒检测失败: {message}")
                print("🔄 跳过唤醒检测，直接进入录音模式（适用于Windows测试环境）")
                
                # 直接进入录音模式
                print("\n📝 开始语音录音识别模式...")
                print("💡 按Ctrl+C退出程序")
                self.start_recording_cycle()
                return
            
            print("\n🎤 语音助手已启动，正在等待唤醒词...")
            print("💡 说出唤醒词来激活语音助手")
            print("⏹️  按Ctrl+C退出程序")
            
            # 启动唤醒检测线程
            self.wake_thread = threading.Thread(target=self.wake_detection_loop)
            self.wake_thread.daemon = True
            self.wake_thread.start()
            
            # 主线程保持运行
            while self.is_running:
                time.sleep(1)
                
        except KeyboardInterrupt:
            print("\n👋 用户中断程序")
        except Exception as e:
            print(f"程序运行过程中发生错误: {e}")
        finally:
            self.stop()
    
    def stop(self):
        """停止语音助手"""
        print("\n🛑 正在停止语音助手...")
        
        self.is_running = False
        
        # 停止唤醒检测
        try:
            self.voice.stop_wake_detection()
        except:
            pass
        
        # 清理资源
        try:
            self.voice.cleanup()
        except:
            pass
        
        print("✅ 语音助手已停止")

def main():
    """主函数"""
    print("="*50)
    print("🤖 语音助手Demo")
    print("功能：唤醒词检测 -> 系统音效 -> VAD录音 -> 语音识别")
    print("="*50)
    
    assistant = VoiceAssistantDemo()
    assistant.start()

if __name__ == "__main__":
    main()
